home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / rkeyboar.cpt / Reactive Keyboard ƒ / Offscreen.inc1.p < prev    next >
Encoding:
Text File  |  1990-05-31  |  26.8 KB  |  875 lines

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    "Skippy White's Famous High Level Off-Screen Map Routines╙
  6. #
  7. #    Offscreen.inc1.p    -    Pascal Source
  8. #
  9. #    Copyright ⌐ 1989 Apple Computer, Inc.
  10. #    All rights reserved.
  11. #    The characters depicted herein (except Skippy White) are fictitious.
  12. #
  13. #    Versions:    
  14. #                1.00                04/89
  15. #
  16. #    Components:    
  17. #                Offscreen.p            April 1, 1988
  18. #                Offscreen.inc1.p    April 1, 1988
  19. #
  20. #    These routines provide a high-level interface to the QuickDraw & Color
  21. #    Manager routines which allow the creation and manipulation of off-screen
  22. #    bitmaps and pixmaps. They are designed to run on any machine with 128K or
  23. #    later ROMs (sorry 64K ROM fans).
  24. #    Note that the design incorporates the idea that you can go along pretending
  25. #    there is an offscreen buffer even when one couldn╒t be allocated, and the
  26. #    calls will do nothing.
  27. #
  28. ------------------------------------------------------------------------------}
  29.  
  30. CONST
  31.     noDepth = -2;
  32.  
  33. TYPE
  34.     PrivateHandle    = ^PrivatePtr;
  35.     PrivatePtr        = ^PrivateRecord;
  36.     PrivateRecord    = RECORD
  37.         requestedBounds    : Rect; {boundary rectangle for the pixmap}
  38.         {used to determine the depth, size, and dimensions of the pixmap}
  39.  
  40.         requestedDepth    : INTEGER; {the requested depth of this map}
  41.         {if > 0, a private device will be created for the map}
  42.  
  43.         requestedColors    : CTabHandle; {color table}
  44.         {our own copy of the color table that was passed in}
  45.  
  46.         requestedPolite    : BOOLEAN; {whether he wanted it polite}
  47.  
  48.         bits            : Handle; {handle to the off-screen bits}
  49.         {if NIL, there is no port or graphics device either}
  50.         notOurs            : BOOLEAN; {TRUE means that we didn╒t create the bits}
  51.         needs32Bits        : BOOLEAN; {you must be in 32 bit memory mode to get to bits}
  52.  
  53.         bitsPort        : CGrafPtr; {GrafPort used for the off-screen map.}
  54.         bitsDevice        : GDHandle; {GDevice associated with the map}
  55.         {private device (if requestedDepth > 0)}
  56.         {(from the device list for requestedDepth = kMaxDepth)}
  57.  
  58.         oldSeed            : LONGINT; {old CTSeed for the pixmap's color table}
  59.         {used to detect color updates}
  60.         
  61.         invalRegion        : RgnHandle; {part of offscreen buffer that is invalid}
  62.  
  63.         drawingPort        : GrafPtr; {supplied to BeginOffscreenDahling}
  64.         savedPort        : GrafPtr; {saves old GrafPort while drawing off screen}
  65.         savedMap        : BitMap; {used to save map when we don╒t use our port}
  66.         savedVisRgn        : RgnHandle; {saves the visRgn from a user-supplied port}
  67.         savedDevice        : GDHandle; {saves old GDevice while drawing off screen}
  68.     END; {PrivateRecord}
  69.  
  70.  
  71.  
  72. VAR
  73.     theMac    : SysEnvRec;
  74.  
  75.  
  76. {---------- low-level service routines used by the rest of Offscreen ----------}
  77.  
  78. {Fill out the pixmap according to the bounds, pixelType, pixelSize, cmpCount,
  79.  cmpSize and pmTable parameters. All fields will be modified.
  80.  The initial pixmap will be copied from the current GDevice╒s pixmap, so you must modify
  81.  the pmap^^.baseAddr before using it.
  82.  dataSize will return with the number of bytes required for the pixel data.
  83.  Note: it is the responsibility of the caller to fill in the baseAddr field
  84.  of the pixmap. You may want to call NewImprovedGBuffer to see if the system will
  85.  allocate the data space and then set baseAddr to point to it.
  86.  This call cannot fail.}
  87. PROCEDURE InitGBufferPixmap(pmap: PixMapHandle; aBounds: Rect; aPixelType, aPixelSize,
  88.      aCmpCount, aCmpSize: UNIV INTEGER; aPMTable: CTabHandle; VAR dataSize: LONGINT);
  89.     
  90. VAR
  91.     theGDevice:    GDHandle;
  92. BEGIN
  93.     theGDevice := GetGDevice;                {get the current device}
  94.     pmap^^:=theGDevice^^.gdPMap^^;            {start out with device╒s pixmap}
  95.     WITH pmap^^ DO BEGIN
  96.         bounds := aBounds;
  97.         pixelType := aPixelType;
  98.         pixelSize := aPixelSize;
  99.          cmpCount := aCmpCount;
  100.         cmpSize := aCmpSize;
  101.         pmTable := aPMTable;
  102.         WITH bounds DO BEGIN
  103.             rowBytes := ((pixelSize * right + 15) DIV 16) * 2;
  104.             {calculate an even # of words}
  105.             dataSize := bottom * LONGINT(rowBytes); {calculate the size}
  106.             rowBytes := rowBytes + $8000;    {flag that it╒s a pixmap}
  107.         END; {WITH}
  108.     END; {WITH}
  109. END; {InitGBufferPixmap}
  110.     
  111. {Fill out a pixmap for use with an onscreen grafPort. It differs from InitGBufferPixmap
  112.  in that it associates the buffer with an onscreen device. This will be the maximim-
  113.  depth device which intersects the globalBounds rectangle. In the most common case
  114.  you will pass in the portRect of a window after converting it to global coordinates.
  115.  If no screen device intersects globalBounds, the function will return FALSE and
  116.  device will return with NIL. If it returns TRUE then device will be a handle to
  117.  the proper GDevice (see StartLeech, below).
  118.  For the other information, see the comments at InitGBufferPixmap.}
  119. FUNCTION InitGBufferScreen(pmap: PixMapHandle; globalBounds: Rect; VAR device:GDHandle;
  120.       VAR dataSize: LONGINT): BOOLEAN;
  121.  
  122. VAR
  123.     savedDevice:    GDHandle;
  124. BEGIN
  125.     InitGBufferScreen := FALSE;
  126.     
  127.     device := GetMaxDevice(globalBounds);        {maximum-depth device for globalBounds}
  128.     IF device <> NIL THEN BEGIN
  129.         savedDevice := GetGDevice;
  130.         
  131.         WITH globalBounds DO
  132.             SetRect(globalBounds, 0, 0, right - left, bottom - top);
  133.         {make globalBounds into a window-ish (zero-based) rectangle}
  134.             
  135.         SetGDevice(device);                        {set to device for InitGBufferPixmap}
  136.         WITH device^^.gdPMap^^ DO
  137.             InitGBufferPixmap(pmap, globalBounds, pixelType, pixelSize,
  138.                  cmpCount, cmpSize, pmTable, dataSize);
  139.             {set up the pixmap}
  140.             
  141.         SetGDevice(savedDevice);
  142.         InitGBufferScreen := TRUE;
  143.     END; {IF device <> NIL}
  144. END; {InitGBufferScreen}
  145.  
  146. {Allocate the data space for the bits of an offscreen buffer. Only do so if
  147.  it can be allocated in an optimum place for graphics use. If the system
  148.  doesn╒t have a superior place to allocate the bits, NIL is returned.
  149.  needs32Bits will return TRUE if the bit space requires 32 bit addressing
  150.  in order to access it. buffNotNeeded will return TRUE if performance will be
  151.  improved by NOT buffering (╥performance╙ means avoiding flicker, etc.)}
  152. FUNCTION NewImprovedGBuffer(dataSize: LONGINT; VAR needs32Bits: BOOLEAN;
  153.             VAR buffNotNeeded: BOOLEAN): Ptr;
  154.  
  155. BEGIN
  156.     NewImprovedGBuffer := NIL;
  157.     needs32Bits := FALSE;
  158.     buffNotNeeded := FALSE;
  159. END; {NewImprovedGBuffer}
  160.  
  161. {Free a buffer allocated via NewImprovedGBuffer.}
  162. PROCEDURE FreeEnhancedGBuffer(bits: Ptr);
  163.  
  164. BEGIN
  165. END; {FreeEnhancedGBuffer}
  166.  
  167. {Fill out the GDevice record and set device^^.gdPMap = pixmap. Errors can occur from not
  168.  being able to build the ITable, etc.}
  169. FUNCTION InitGBufferDevice(device: GDHandle; pmap: PixMapHandle): OSErr;
  170.  
  171. VAR
  172.     preferredResolution:    INTEGER;
  173.     inverseTable:             ITabHandle;
  174.  
  175.     PROCEDURE Out(error: OSErr);
  176.     BEGIN {Out}
  177.         IF error <> noErr THEN BEGIN
  178.             DisposHandle(Handle(device^^.gdITable));    {get rid of the inverse table}
  179.             InitGBufferDevice := error;
  180.             EXIT(InitGBufferDevice);
  181.         END; {IF error <> noErr}
  182.     END; {Out}
  183.  
  184. BEGIN
  185.     device^^.gdITable := NIL;
  186.     inverseTable := ITabHandle(NewHandle(0));    {create the inverse table stub}
  187.     Out(MemError);                                {bail if couldn╒t make the handle}
  188.     MakeITable(pmap^^.pmTable, inverseTable, preferredResolution);
  189.     Out(QDError);                                {bail if MakeITable failed}
  190.     
  191.     preferredResolution := GetMainDevice^^.gdResPref; {preferred resolution ??? is this cool?}
  192.     
  193.     WITH device^^ DO BEGIN
  194.         gdType := clutType;                        {no private devices for direct ones, right?}    
  195.         gdITable := inverseTable;
  196.         gdResPref := preferredResolution;
  197.         gdFlags := 2 ** noDriver;
  198.         gdPMap := pmap;
  199.         gdRect := pmap^^.bounds;
  200.     END; {WITH}
  201.     
  202.     InitGBufferDevice := noErr;
  203. END; {InitGBufferDevice}
  204.  
  205.  
  206. {---------- end of low-level service routines ----------}
  207.  
  208.  
  209. PROCEDURE InitOffscreen;
  210. {128K ROMs and new enough Palette Manager}
  211.  
  212. VAR
  213.     error    : OSErr;
  214.  
  215. BEGIN {InitOffscreen}
  216.     error := SysEnvirons(1, theMac);
  217. END; {InitOffscreen}
  218.  
  219.  
  220. (* ??? commented out since we no longer use it (but someone may want it, so should we provide it as a utility?)
  221. FUNCTION GetMaxAreaDevice(globalRect: Rect): GDHandle;
  222. {Find the greatest overlap device for the given global rectangle.}
  223.  
  224. VAR
  225.     area            : LONGINT;
  226.     maxArea            : LONGINT;
  227.     device            : GDHandle;
  228.     intersection    : Rect;
  229.  
  230. BEGIN {GetMaxAreaDevice}
  231.     GetMaxAreaDevice := NIL;
  232.  
  233.     maxArea := 0;
  234.  
  235.     device := GetDeviceList;
  236.     WHILE device <> NIL DO BEGIN
  237.         IF TestDeviceAttribute(device, screenDevice) THEN
  238.             IF TestDeviceAttribute(device, screenActive) THEN
  239.                 IF SectRect(globalRect, device^^.gdRect, intersection) THEN BEGIN
  240.                     WITH intersection DO
  241.                         area := LONGINT(right - left) * LONGINT(bottom - top);
  242.                     IF area > maxArea THEN BEGIN
  243.                         GetMaxAreaDevice := device;
  244.                         maxArea := area;
  245.                     END; {IF area > maxArea}
  246.                 END; {IF SectRect...}
  247.         device := GetNextDevice(device);
  248.     END; {WHILE device <> NIL}
  249. END; {GetMaxAreaDevice}
  250. *)
  251.  
  252.  
  253.  
  254. FUNCTION MakeCleanColorTable(colors: CTabHandle; depth: INTEGER;
  255.                              VAR table: CTabHandle): OSErr;
  256. {This call takes a handle to a list of colors to be included in a color table.
  257.  It returns a table suitable for use for a PixMap, with black and white at the
  258.  correct offsets. If there is no default color table for the depth in question,
  259.  it returns NIL for the table.}
  260.  
  261.     FUNCTION IsWhite(rgb: RGBColor): BOOLEAN;
  262.     BEGIN {IsWhite}
  263.         IsWhite := (rgb.red = $FFFF) & (rgb.green = $FFFF) & (rgb.blue = $FFFF);
  264.     END; {IsWhite}
  265.  
  266.     FUNCTION IsBlack(rgb: RGBColor): BOOLEAN;
  267.     BEGIN {IsBlack}
  268.         IsBlack := (rgb.red = 0) & (rgb.green = 0) & (rgb.blue = 0);
  269.     END; {IsBlack}
  270.  
  271. VAR
  272.     tableIndex    : INTEGER;
  273.     colorIndex    : INTEGER;
  274.     aColor        : RGBColor;
  275.  
  276. BEGIN {MakeCleanColorTable}
  277.     table := GetCTable(depth);
  278.     IF table = NIL THEN
  279.         MakeCleanColorTable := memFullErr
  280.     ELSE
  281.         WITH table^^ DO BEGIN
  282.             ctSeed := GetCTSeed; {get a fresh seed; we'll change the color table}
  283.  
  284.             tableIndex := 1; {skip white in table we are making}
  285.             colorIndex := 0; {start with first color}
  286.  
  287.             REPEAT
  288.                 IF colorIndex > colors^^.ctSize THEN
  289.                     Leave; {done with all the colors}
  290.                 aColor := colors^^.ctTable[colorIndex].rgb;
  291.                 colorIndex := colorIndex + 1; {advance to next color}
  292.                 IF IsWhite(aColor) THEN
  293.                     Cycle; {this one is white, try another}
  294.                 IF IsBlack(aColor) THEN
  295.                     Cycle; {this one is black, try another}
  296.  
  297.         {$PUSH} {$R-}
  298.                 ctTable[tableIndex].rgb := aColor; {use this color}
  299.         {$POP}
  300.                 tableIndex := tableIndex + 1;
  301.             UNTIL tableIndex > ctSize - 1; {until the table is full}
  302.  
  303.             MakeCleanColorTable := noErr;
  304.         END; {WITH}
  305. END; {MakeCleanColorTable}
  306.  
  307.  
  308.  
  309. PROCEDURE DeallocateBits(offscreenHandle: Handle);
  310. {Get rid of the bits from an off-screen map.}
  311.  
  312. BEGIN {DeallocateBits}
  313.     IF offscreenHandle <> NIL THEN
  314.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  315.             IF NOT notOurs THEN
  316.                 DisposHandle(bits) {get rid of the bits}
  317.             ELSE
  318.                 FreeEnhancedGBuffer(Ptr(bits));
  319.             bits := NIL;
  320.  
  321.             IF bitsPort <> NIL THEN BEGIN
  322.                 IF bitsPort^.visRgn <> NIL THEN BEGIN {visRgn of NIL means port is not open yet}
  323.                     IF requestedDepth > 0 THEN {if we created a private device and color table}
  324.                         DisposHandle(Handle(bitsPort^.portPixMap^^.pmTable)); {get rid of the colors}
  325.                     ClosePort(GrafPtr(bitsPort));
  326.             
  327.                 DisposeRgn(invalRegion); {get rid of the invalidation region}
  328.                 END; {IF ...bitsPort^.visRgn <> NIL}
  329.                 DisposPtr(Ptr(bitsPort));
  330.             END; {IF bitsPort <> NIL}
  331.             bitsPort := NIL;
  332.  
  333.             IF bitsDevice <> NIL THEN
  334.                 IF requestedDepth > 0 THEN BEGIN {if we created a private device}
  335.                     bitsDevice^^.gdPMap := NIL; {the pixmap was owned by the port}
  336.                     DisposGDevice(bitsDevice);
  337.                     bitsDevice := NIL;
  338.                 END; {IF requestedDepth > 0}
  339.         END; {WITH}
  340. END; {DeallocateBits}
  341.  
  342.  
  343.  
  344. FUNCTION GetMap(offscreenHandle: Handle): BitMapPtr;
  345. {Get a BitMapPtr for the bits in the offscreen handle.
  346.  This is also the best way to see if the bits are allocated.
  347.  Note that this has a side-effect of setting up the baseAddr properly.}
  348.  
  349. VAR
  350.     map    : BitMapPtr;
  351.  
  352. BEGIN {GetMap}
  353.     map := NIL;
  354.  
  355.     IF offscreenHandle <> NIL THEN {if we have bits}
  356.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  357.             IF bits <> NIL THEN {if we seem to have bits, make sure that we really do}
  358.                 IF StripAddress(bits^) = NIL THEN
  359.                     DeallocateBits(offscreenHandle)
  360.                 ELSE BEGIN
  361.                     {find the map}
  362.                     WITH bitsPort^ DO
  363.                         IF portVersion > 0 THEN {old-style port?}
  364.                             map := @portPixMap {this is really portBits!}
  365.                         ELSE
  366.                             map := BitMapPtr(portPixMap^);
  367.                     IF notOurs THEN
  368.                         map^.baseAddr := Ptr(bits)
  369.                     ELSE
  370.                         map^.baseAddr := bits^;
  371.                 END; {ELSE}
  372.         END; {WITH}
  373.     GetMap := map;
  374. END; {GetMap}
  375.  
  376.  
  377.  
  378. FUNCTION GetBitsHandle(offscreenHandle: Handle): Handle;
  379. BEGIN
  380.     GetBitsHandle := NIL;
  381.     IF offscreenHandle <> NIL THEN
  382.         WITH PrivateHandle(offscreenHandle)^^ DO
  383.             IF NOT notOurs THEN
  384.                 GetBitsHandle := bits;
  385. END; {GetBitsHandle}
  386.  
  387.  
  388.  
  389. FUNCTION AllocateBits(offscreenHandle: Handle; VAR buffNotNeeded: BOOLEAN): OSErr;
  390. {Allocate the bits, port, device, etc. for the off-screen map.
  391.  If an error code is returned, the handle will be in the "dormant" stage. ??? define
  392.  If the bits are already allocated, then nothing will happen.}
  393.  
  394. VAR
  395.     oldPort                : GrafPtr;
  396.     bounder                : Rect;
  397.     map                    : BitMapPtr;
  398.     bitsSize            : LONGINT;
  399.     colors                : CTabHandle;
  400.     preferredResolution    : INTEGER;
  401.     inverseTable        : ITabHandle;
  402.     totalSpace            : LONGINT;
  403.     contiguousSpace        : LONGINT;
  404.     use32Bits            : BOOLEAN;
  405.  
  406.     PROCEDURE Out(error: OSErr);
  407.     BEGIN {Out}
  408.         IF error <> noErr THEN BEGIN
  409.             HUnlock(offscreenHandle);
  410.             DeallocateBits(offscreenHandle);
  411.             SetPort(oldPort);
  412.             AllocateBits := error;
  413.             EXIT(AllocateBits);
  414.         END; {IF error <> noErr}
  415.     END; {Out}
  416.  
  417. BEGIN {AllocateBits}
  418.     IF offscreenHandle <> NIL THEN
  419.         IF GetMap(offscreenHandle) = NIL THEN BEGIN {if the bits are not already allocated}
  420.             GetPort(oldPort);
  421.  
  422.             MoveHHi(offscreenHandle); {fly the handle high in the heap since we lock it down}
  423.             HLock(offscreenHandle);
  424.             WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  425.                 bitsPort := CGrafPtr(NewPtrClear(SIZEOF(GrafPort))); {create a new port}
  426.                 {NewPtrClear means that bits := NIL, invalRegion := NIL, notOurs := FALSE, etc.}
  427.                 Out(MemError);
  428.  
  429.                 WITH requestedBounds DO
  430.                     SetRect(bounder, 0, 0, right - left, bottom - top);
  431.  
  432.                 IF theMac.hasColorQD THEN
  433.                     WITH bitsPort^ DO BEGIN
  434.                         OpenCPort(bitsPort); {always use a color port}
  435.                         portPixMap^^.pmTable := NIL; {in case we fail before we make a color table}
  436.                         
  437.                         IF requestedDepth > 0 THEN BEGIN {if there is a private device}
  438.                             {make a color table from the requested colors or a resource file or ROM}
  439.                             Out(MakeCleanColorTable(requestedColors, requestedDepth, colors));
  440.                             
  441.                             {now set up the pixmap of the port}
  442.                             InitGBufferPixmap(portPixMap, bounder, chunky, requestedDepth, 1, requestedDepth,
  443.                                 colors, {returns} bitsSize);
  444.         
  445.                             {make a private device}
  446.                             bitsDevice := GDHandle(NewHandleClear(SIZEOF(GDevice)));
  447.                             Out(MemError); {must have failed because of heap paucity}
  448.         
  449.                             inverseTable := ITabHandle(NewHandle(0)); {create the inverse table}
  450.                             Out(MemError);
  451.                             preferredResolution := GetMainDevice^^.gdResPref; {preferred resolution ??? is this cool?}
  452.         
  453.                             WITH bitsDevice^^ DO BEGIN {set up the other fields of the device}
  454.                                 gdType := clutType;
  455.                                 gdITable := inverseTable;
  456.                                 gdResPref := preferredResolution;
  457.                                 gdFlags := 2 ** noDriver;
  458.                                 gdPMap := portPixMap;
  459.                             END; {WITH}
  460.                             InitGDevice(0, -1, bitsDevice); {sets gdRect from pixmap now; in future may do more}
  461.                             MakeITable(colors, inverseTable, preferredResolution);
  462.                             Out(QDError); {MakeITable failed}
  463.                         END ELSE BEGIN {when there is no private device}
  464.                                 {get the max. area device and set up the port╒s pixmap accordingly}
  465.                                 IF NOT InitGBufferScreen(portPixMap, requestedBounds, bitsDevice, {returns} bitsSize) THEN
  466.                                     Out(noDeviceIntersectErr); {no devices intersect bounds}
  467.                                     
  468.                                 oldSeed := portPixMap^^.pmTable^^.ctSeed; {used to detect color updates}
  469.                         END;
  470.                         
  471.                     map := BitMapPtr(portPixMap^); {point to the map so we can set the portRect & visRgn}
  472.  
  473. {!!! ??? Do we need to call ForeColor and BackColor on the CGrafPort to set it up right?}
  474.  
  475.                     END {WITH}
  476.                 ELSE BEGIN {Classic QuickDraw}
  477.                     OpenPort(GrafPtr(bitsPort));
  478.                     WITH GrafPtr(bitsPort)^, portBits DO BEGIN
  479.                         bounds := bounder;
  480.                         {figure out how much space we need for our bits}
  481.                         WITH bounds DO BEGIN
  482.                             rowBytes := ((right + 15) DIV 16) * 2;
  483.                             {calculate an even # of words}
  484.                             bitsSize := bottom * LONGINT(rowBytes); {calculate the size}
  485.                         END; {WITH}
  486.                         map := @portBits; {point to the map so we can set it up}
  487.                     END; {WITH}
  488.                 END;
  489.                     
  490.                 WITH bitsPort^ DO BEGIN
  491.                     portRect := map^.bounds; {port's portRect is same as bounds}
  492.                     RectRgn(visRgn, portRect); {the visRgn must be changed}
  493.                     invalRegion := NewRgn;
  494.                     Out(MemError);
  495.                     CopyRgn(visRgn, invalRegion);
  496.                 END; {WITH}
  497.                 
  498.                 bits := Handle(NewImprovedGBuffer(bitsSize, needs32Bits, buffNotNeeded));
  499.                 notOurs := bits <> NIL;
  500.                 IF NOT notOurs THEN BEGIN
  501.                     IF requestedPolite THEN
  502.                         contiguousSpace := MaxBlock {make sure we have room without purging}
  503.                     ELSE
  504.                         PurgeSpace(totalSpace, contiguousSpace); {make sure we have room with purging}
  505.                     IF bitsSize + kOffscreenReserve > contiguousSpace THEN
  506.                         {if there is not enough room for the bits & reserve block, error out}
  507.                         Out(memFullErr);
  508.                     bits := NewHandleClear(bitsSize); {get space for the bits}
  509.                     Out(MemError);
  510.                     IF requestedPolite THEN
  511.                         HPurge(bits); {keep bits purgeable if we are polite}
  512.                 END;
  513.                 
  514.             END; {WITH}
  515.             HUnlock(offscreenHandle);
  516.  
  517.             SetPort(oldPort);
  518.         END; {IF GetMap(offscreenHandle) = NIL}
  519.  
  520.     AllocateBits := noErr;
  521. END; {AllocateBits}
  522.  
  523.  
  524.  
  525. PROCEDURE DisposeOffscreen(offscreenHandle: Handle);
  526. {Get rid of everything, including offscreenHandle itself.}
  527.  
  528. BEGIN {DisposeOffscreen}
  529.     IF offscreenHandle <> NIL THEN BEGIN
  530.         DeallocateBits(offscreenHandle);
  531.         DisposHandle(Handle(PrivateHandle(offscreenHandle)^^.requestedColors));
  532.         DisposHandle(offscreenHandle);
  533.     END; {IF offscreenHandle <> NIL}
  534. END; {DisposeOffscreen}
  535.  
  536.  
  537.  
  538. FUNCTION NewOffscreen(bounds: Rect; depth: INTEGER; colors: CTabHandle;
  539.                       memoryPolite: BOOLEAN; VAR buffNotNeeded: BOOLEAN; VAR offscreenHandle: Handle): OSErr;
  540. {Create an offscreenHandle. Return NIL if there is an error.}
  541.  
  542. VAR
  543.     aHandle    : Handle;
  544.  
  545.     PROCEDURE Out(error: OSErr);
  546.     BEGIN {Out}
  547.         IF error <> noErr THEN BEGIN
  548.             DisposHandle(aHandle); {dispose handle if we made it}
  549.             NewOffscreen := error;
  550.             offscreenHandle := NIL;
  551.             EXIT(NewOffscreen);
  552.         END; {IF error <> noErr}
  553.     END; {Out}
  554.  
  555. BEGIN {NewOffscreen}
  556.     aHandle := NewHandleClear(SizeOf(PrivateRecord)); {make the private block}
  557.     Out(MemError);
  558.  
  559.     MoveHHi(aHandle); {fly this high so it doesn't fragment the heap}
  560.     HLock(aHandle);
  561.     WITH PrivateHandle(aHandle)^^ DO BEGIN
  562.         requestedBounds := bounds;
  563.         IF theMac.hasColorQD THEN
  564.             requestedDepth := depth
  565.         ELSE
  566.             requestedDepth := noDepth;
  567.         IF requestedDepth > 0 THEN BEGIN {if we need a private device}
  568.             Out(HandToHand(Handle(colors)));
  569.             requestedColors := colors;
  570.         END;
  571.         requestedPolite := memoryPolite;
  572.     END; {WITH}
  573.     HUnlock(aHandle);
  574.  
  575.     {now, we have filled in all of the fields of offscreenHandle}
  576.     NewOffscreen := AllocateBits(aHandle, buffNotNeeded);
  577.     offscreenHandle := aHandle;
  578. END; {NewOffscreen}
  579.  
  580.  
  581.  
  582. FUNCTION CheckOffscreen(offscreenHandle: Handle; VAR drawNeeded: BOOLEAN): OSErr;
  583.  
  584. VAR
  585.     error            : OSErr;
  586.     newSeed            : LONGINT;
  587.     buffNotNeeded    : BOOLEAN;
  588.  
  589. BEGIN {CheckOffscreen}
  590.     drawNeeded := TRUE; {default we must draw if offscreen invalid}
  591.     error := noErr; {no error either}
  592.     IF offscreenHandle <> NIL THEN
  593.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  594.             IF requestedDepth = kMaxDepth THEN {if it's not a private device}
  595.                 IF oldSeed <> bitsDevice^^.gdPMap^^.pmTable^^.ctSeed THEN {we have a color update, Houston}
  596.                     DeallocateBits(offscreenHandle);
  597.  
  598.             drawNeeded := GetMap(offscreenHandle) = NIL;
  599.             error := AllocateBits(offscreenHandle, buffNotNeeded); {allocate if deallocated}
  600.         END; {WITH}
  601.             
  602.     IF GetMap(offscreenHandle) <> NIL THEN
  603.         drawNeeded := drawNeeded | (NOT EmptyRgn(PrivateHandle(offscreenHandle)^^.invalRegion));
  604.         {can╒t use the width for invalRegion since we may have done an AllocateBits}
  605.         
  606.     CheckOffscreen := error;
  607. END; {CheckOffscreen}
  608.  
  609.  
  610. PROCEDURE ValidRectOffscreen(offscreenHandle: Handle; window: WindowPtr; validRect: Rect);
  611.  
  612. VAR
  613.     validRgn    : RgnHandle;
  614.  
  615. BEGIN
  616.     validRgn := NewRgn;
  617.     RectRgn(validRgn, validRect);
  618.     ValidRgnOffscreen(offscreenHandle, window, validRgn);
  619.     DisposeRgn(validRgn);
  620. END; {ValidRectOffscreen}
  621.  
  622.  
  623. PROCEDURE ValidRgnOffscreen(offscreenHandle: Handle; window: WindowPtr; validRegion: RgnHandle);
  624.  
  625. VAR
  626.     savedPort    : GrafPtr;
  627.     
  628. BEGIN
  629.     IF window <> NIL THEN BEGIN
  630.         GetPort(savedPort);
  631.         SetPort(window);
  632.         ValidRgn(validRegion);
  633.         SetPort(savedPort);
  634.     END;
  635.     IF GetMap(offscreenHandle) <> NIL THEN
  636.         WITH PrivateHandle(offscreenHandle)^^ DO
  637.             DiffRgn(invalRegion, validRegion, invalRegion);
  638. END; {ValidRgnOffscreen}
  639.  
  640.  
  641. PROCEDURE InvalRectOffscreen(offscreenHandle: Handle; window: WindowPtr; invalidRect: Rect);
  642.  
  643. VAR
  644.     invalidRgn    : RgnHandle;
  645.  
  646. BEGIN
  647.     invalidRgn := NewRgn;
  648.     RectRgn(invalidRgn, invalidRect);
  649.     InvalRgnOffscreen(offscreenHandle, window, invalidRgn);
  650.     DisposeRgn(invalidRgn);
  651. END; {InvalRectOffscreen}
  652.  
  653.  
  654. PROCEDURE InvalRgnOffscreen(offscreenHandle: Handle; window: WindowPtr; invalidRgn: RgnHandle);
  655.  
  656. VAR
  657.     savedPort    : GrafPtr;
  658.     
  659. BEGIN
  660.     IF window <> NIL THEN BEGIN
  661.         GetPort(savedPort);
  662.         SetPort(window);
  663.         InvalRgn(invalidRgn);
  664.         SetPort(savedPort);
  665.     END;
  666.     IF GetMap(offscreenHandle) <> NIL THEN
  667.         WITH PrivateHandle(offscreenHandle)^^ DO
  668.             UnionRgn(invalRegion, invalidRgn, invalRegion);
  669. END; {ValidRgnOffscreen}
  670.  
  671.  
  672. FUNCTION CheckBoundsOffscreen(offscreenHandle: Handle; newBounds: Rect;
  673.                        VAR drawNeeded: BOOLEAN): OSErr;
  674.  
  675. VAR
  676.     somethingChanged    : BOOLEAN;
  677.     rightDevice            : GDHandle;
  678.  
  679. BEGIN {CheckBoundsOffscreen}
  680.     IF offscreenHandle <> NIL THEN
  681.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  682.             {check to see if the size of the port changed}
  683.             WITH newBounds, bitsPort^.portRect.botRight DO
  684.                 somethingChanged := (right - left <> h) | (bottom - top <> v);
  685.  
  686.             IF NOT somethingChanged THEN BEGIN
  687.                 {check to see if the depth of the screen changed}
  688.                 rightDevice := NIL;
  689.                 IF requestedDepth = kMaxDepth THEN
  690.                     rightDevice := GetMaxDevice(newBounds);
  691.                 somethingChanged := (rightDevice <> NIL) & (rightDevice <> bitsDevice);
  692.             END;
  693.  
  694.             IF somethingChanged THEN BEGIN {we have a color update, Houston}
  695.                 DeallocateBits(offscreenHandle);
  696.                 requestedBounds := newBounds;
  697.             END;
  698.         END; {WITH}
  699.     CheckBoundsOffscreen := CheckOffscreen(offscreenHandle, drawNeeded);
  700. END; {CheckBoundsOffscreen}
  701.  
  702.  
  703.  
  704. PROCEDURE BeginOffscreenDrawing(offscreenHandle: Handle; port: GrafPtr);
  705. BEGIN {BeginOffscreenDrawing}
  706.     IF GetMap(offscreenHandle) <> NIL THEN
  707.     {note side effect here of setting up map for drawing (stuffing baseAddr)}
  708.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  709.             IF NOT notOurs THEN {if we, not the system, allocated the bits}
  710.                 {lock the bits down}
  711.                 HLock(bits);
  712.  
  713.             {we need the pixmap locked while we are using it}
  714.             IF theMac.hasColorQD THEN
  715.                 HLock(Handle(bitsPort^.portPixMap));
  716.  
  717.             drawingPort := port;
  718.             GetPort(savedPort);
  719.             
  720.             {set up the port and the device}
  721.             
  722.             IF drawingPort = NIL THEN
  723.                 SetPort(GrafPtr(bitsPort))
  724.             ELSE BEGIN
  725.                 SetPort(drawingPort);
  726.                 {save the visRgn and replace it with our private one}
  727.                 savedVisRgn := drawingPort^.visRgn;
  728.                 drawingPort^.visRgn := bitsPort^.visRgn;
  729.                 
  730.                 savedMap := drawingPort^.portBits;
  731.                 
  732.                 IF theMac.hasColorQD THEN
  733.                     SetPortPix(bitsPort^.portPixMap)
  734.                 ELSE
  735.                     SetPortBits(GrafPtr(bitsPort)^.portBits);
  736.             END; {for caller-supplied port case}
  737.             
  738.             IF theMac.hasColorQD THEN BEGIN
  739.                 savedDevice := GetGDevice;
  740.                 SetGDevice(bitsDevice);
  741.             END; {IF theMac.hasColorQD}
  742.             
  743.             WITH thePort^ DO
  744.                 {intersect the invalidated offscreen region with private visRgn}
  745.                 SectRgn(invalRegion, visRgn, visRgn);
  746.         END; {WITH}
  747. END; {BeginOffscreenDrawing}
  748.  
  749.  
  750.  
  751. PROCEDURE EndOffscreenDrawing(offscreenHandle: Handle);
  752.  
  753. TYPE
  754.     PMapHandlePtr    = ^PixMapHandle;
  755.     
  756. BEGIN {EndOffscreenDrawing}
  757.     IF GetMap(offscreenHandle) <> NIL THEN
  758.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  759.             {restore the map, device, port}
  760.             
  761.             IF drawingPort <> NIL THEN BEGIN
  762.                 SetPort(drawingPort);
  763.                 drawingPort^.visRgn := savedVisRgn; {restore original visRgn}
  764.                 
  765.                 IF theMac.hasColorQD THEN
  766.                     SetPortPix(PMapHandlePtr(@savedMap)^)
  767.                 ELSE
  768.                     SetPortBits(savedMap);
  769.             END; {for caller-supplied port case}                
  770.                 
  771.             IF theMac.hasColorQD THEN
  772.                 SetGDevice(savedDevice);
  773.                 
  774.             SetPort(savedPort);
  775.  
  776.             {we only need the pixmap locked while we are using it}
  777.             IF theMac.hasColorQD THEN
  778.                 HUnlock(Handle(bitsPort^.portPixMap));
  779.  
  780.             IF NOT notOurs THEN
  781.                 {unlock the bits so they can float}
  782.                 HUnlock(bits);
  783.             
  784.             WITH bitsPort^ DO
  785.                 RectRgn(visRgn, portRect); {set private visRgn back to full size}
  786.         END; {WITH}
  787. END; {EndOffscreenDrawing}
  788.  
  789.  
  790. PROCEDURE BeginUpdateOffscreen(offscreenHandle: Handle; window: WindowPtr);
  791. BEGIN
  792.     BeginUpdate(window);
  793.     IF GetMap(offscreenHandle) <> NIL THEN BEGIN
  794.         BeginOffscreenDrawing(offscreenHandle, window); {╒sects invalRegion with visRgn}
  795.         {validate the entire contents of the offscreen buffer}
  796.         ValidRectOffscreen(offscreenHandle, NIL, PrivateHandle(offscreenHandle)^^.bitsPort^.portRect);
  797.     END;
  798. END; {BeginUpdateOffscreen}
  799.  
  800.  
  801. PROCEDURE EndUpdateOffscreen(offscreenHandle: Handle; window: WindowPtr);
  802. BEGIN
  803.     IF GetMap(offscreenHandle) <> NIL THEN BEGIN
  804.         EndOffscreenDrawing(offscreenHandle);
  805.         
  806.         SetPort(window);
  807.         ForeColor(blackColor);
  808.         BackColor(whiteColor);
  809.         {now copy from the offscreen buffer to the window (clipped to window╒s visRgn)}
  810.         WITH PrivateHandle(offscreenHandle)^^ DO
  811.             CopyBits(GrafPtr(bitsPort)^.portBits, window^.portBits, bitsPort^.portRect,
  812.                 window^.portRect, srcCopy, NIL);
  813.     END;
  814.     EndUpdate(window);
  815. END; {EndUpdateOffscreen}
  816.  
  817.  
  818. {Create an off-screen pixel map for a port.
  819.  It will always use kMaxDepth and always be memoryPolite.}
  820.  
  821. FUNCTION NewOffscreenForWindow(window: WindowPtr; VAR buffNotNeeded: BOOLEAN;
  822.                                VAR offscreenHandle: Handle): OSErr;
  823.  
  824. VAR
  825.     savedPort    : GrafPtr;
  826.     globalRect    : Rect;
  827.  
  828. BEGIN {NewOffscreenForWindow}
  829.     SetPort(window);
  830.  
  831.     globalRect := window^.portRect; {calculate a global rect for this window}
  832.     WITH globalRect DO BEGIN
  833.         LocalToGlobal(topLeft);
  834.         LocalToGlobal(botRight);
  835.     END;
  836.  
  837.     NewOffscreenForWindow := NewOffscreen(globalRect, kMaxDepth, NIL, TRUE,
  838.         buffNotNeeded, offscreenHandle);
  839. END; {NewOffscreenForWindow}
  840.  
  841.  
  842.  
  843. (* ??? looks like these aren╒t needed╔
  844. {Begin an update for the window specified, using the specified off-screen
  845.  handle for bits. This sets the port to the window! ╔and temporarily nukes
  846.  the visRgn! ??? @@@ !!!}
  847.  
  848. PROCEDURE BeginOffscreenUpdate(offscreenHandle: Handle; window: WindowPtr);
  849. BEGIN {BeginOffscreenUpdate}
  850.     BeginOffscreenDrawing(offscreenHandle); {???!!! 
  851.     SetPort(window); {override the offscreen port which was set by BOU}
  852.     BeginUpdate(window);
  853.     
  854. END; {BeginOffscreenUpdate}
  855.  
  856.  
  857.  
  858. {Finish drawing to an off-screen port. Also copy the bits from the
  859.  port to the screen.}
  860.  
  861. PROCEDURE EndOffscreenUpdate(offscreenHandle: Handle; window: WindowPtr);
  862.  
  863.  
  864.  
  865. {Call this when the rectangle of the off-screen map needs to change, perhaps
  866.  due to a window moving on the main screen. This will also check for any change
  867.  in depth.}
  868.  
  869. FUNCTION UpdateOffscreenForWindow(offscreenHandle: Handle; window: WindowPtr;
  870.                                   VAR drawNeeded: BOOLEAN): OSErr;
  871. *)
  872.  
  873.  
  874. END. {Offscreen}
  875.